using System;
using System.Reflection;

namespace DataBroker.Dao
{
    /// <summary>
    /// Helper functions to access Entity properties.
    /// </summary>
    /// <remarks>$Id$</remarks>
    public sealed class PropertyHelper
    {
		
        /// <summary>
        /// If the property has a TypeAttribute defined, return it. 
        /// Otherwise, return the type of the property itself.
        /// </summary>
        /// <param name="prop"></param>
        /// <returns></returns>
        public static Type GetPropertyAttributeType(PropertyInfo prop)
        {
            if (prop == null)
                return Type.Missing.GetType();
            object[] classAttributes;
            classAttributes = prop.GetCustomAttributes(typeof(TypeAttribute), false);
            if (classAttributes.GetLength(0) != 0)
            {
                return ((TypeAttribute) classAttributes[0]).TypeName; 
            }
            return prop.PropertyType;
        }

        ///<summary>
        /// Get precision attribute for property
        ///</summary>
        ///<param name="prop"></param>
        ///<returns></returns>
        public static int GetPropertyPrecision(PropertyInfo prop)
        {
            object[] classAttributes;
            classAttributes = prop.GetCustomAttributes(typeof(PrecisionAttribute), false);
            if (classAttributes.GetLength(0) != 0)
            {
                return ((PrecisionAttribute) classAttributes[0]).Precision; 
            }
            return 0;
        }

        public static int GetPropertyMaxLength(PropertyInfo prop)
        {
            object[] classAttributes;
            classAttributes = prop.GetCustomAttributes(typeof(MaxLengthAttribute), false);
            if (classAttributes.GetLength(0) != 0)
            {
                return ((MaxLengthAttribute) classAttributes[0]).MaxLength; 
            }
            return 0;
        }

    }

    /// <summary>
    /// Attribute that stores the type a collection will have in its elements.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class TypedCollectionAttribute : Attribute
    {
        private Type _CollectionType;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="collectionType">Data type for collection item.</param>
        public TypedCollectionAttribute(Type collectionType)
        {
            _CollectionType = collectionType;
        }

        /// <summary>
        /// Data type for collection item.
        /// </summary>
        public Type CollectionType
        {
            get { return _CollectionType; }
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as Display Only (no updates)
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class DisplayOnlyAttribute : Attribute
    {
    }

    /// <summary>
    /// Attribute tag to indicate that this TypedContainedAttribute 
    /// is not to have its properties expanded
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class DontIncludeColumnsAttribute : Attribute
    {
    }

    /// <summary>
    /// Attribute tag to define the (.net) type of the property.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class TypeAttribute : Attribute
    {
        private Type typeName;
        /// <summary>
        /// Attribute that defines the .NET type for the entity property.
        /// </summary>
        /// <param name="typeName">Type for attribute.</param>
        public TypeAttribute (Type typeName)
        {
            this.typeName = typeName;
        }

        /// <summary>
        /// Type of Entity property.
        /// </summary>
        public Type TypeName
        {
            get
            {
                return typeName;
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as not in tableName.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class NotInTableAttribute : Attribute
    {
        private string _DatabaseVersion;

        public string DatabaseVersion
        {
            get { return _DatabaseVersion; }
            set { _DatabaseVersion = value; }
        }


        public NotInTableAttribute()
        {
            DatabaseVersion = "";
        }


        public NotInTableAttribute(string databaseVersion)
        {
            DatabaseVersion = databaseVersion;
        }

        /// <summary>
        /// String description for class
        /// </summary>
        /// <returns>"Column not in tableName"</returns>
        public override string ToString()
        {
            return "Column not in tableName";
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as a deletion indicator.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class DeleteIndicatorAttribute : Attribute
    {
        /// <summary>
        /// String description for class
        /// </summary>
        /// <returns>"Column not in tableName"</returns>
        public override string ToString()
        {
            return "Column is a Delete indicator.";
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as a deletion indicator.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class ZeroIsNullAttribute : Attribute
    {
        /// <summary>
        /// String description for class
        /// </summary>
        /// <returns>"Column not in tableName"</returns>
        public override string ToString()
        {
            return "Column is Null when value is ZERO.";
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as being identity.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class IsIdentityAttribute : Attribute
    {
        /// <summary>
        /// String description for class
        /// </summary>
        /// <returns>"Is Identity"</returns>
        public override string ToString()
        {
            return "Is Identity";
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as not nullable.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class IsNotNullableAttribute : Attribute
    {
        /// <summary>
        /// String description for class
        /// </summary>
        /// <returns>"Is Not Nullable"</returns>
        public override string ToString()
        {
            return "Is Not Nullable";
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as participating in the Primary Key.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class IsPKAttribute : Attribute
    {
    }

    /// <summary>
    /// Attribute tag to define a label for a column to refer on User Interface.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class LabelAttribute : Attribute
    {
        string _label;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="label">Name for column label.</param>
        public LabelAttribute(string label)
        {
            _label = label;
        }
        /// <summary>
        /// Name for columnName.
        /// </summary>
        public string ColumnName
        {
            get
            {
                return (_label);
            }
        }
    }

    /// <summary>
    /// Attribute tag to define a label for a column to refer on User Interface.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public sealed class EntityLabelAttribute : Attribute
    {
        string _label;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="label">Name for column label.</param>
        public EntityLabelAttribute(string label)
        {
            _label = label;
        }
        /// <summary>
        /// Name for columnName.
        /// </summary>
        public string EntityName
        {
            get
            {
                return (_label);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as mandatory when referred in a User Interface.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class IsMandatoryAttribute : Attribute
    {
    }

    /// <summary>
    /// Attribute tag to mark the maximum length for a columnName.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class MaxLengthAttribute : Attribute
    {
        private int maxLength;
        /// <summary>
        /// Max Length constructor.
        /// </summary>
        /// <param name="maxLength">Maximum length for columnName.</param>
        public MaxLengthAttribute(int maxLength)
        {
            this.maxLength = maxLength;
        }
        /// <summary>
        /// Max Length property.
        /// </summary>
        public int MaxLength
        {
            get
            {
                return (maxLength);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark the maximum length for a columnName.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class PrecisionAttribute : Attribute
    {
        int precision;
        /// <summary>
        /// Precision constructor.
        /// </summary>
        /// <param name="precision">Precision for entity attribute.</param>
        public PrecisionAttribute(int precision)
        {
            this.precision = precision;
        }
        /// <summary>
        /// Precision property.
        /// </summary>
        public int Precision
        {
            get
            {
                return (precision);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark a name for a columnName in a database tableName.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class ColumnNameAttribute : Attribute
    {
        string _columnName;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="columnName">Name for columnName.</param>
        public ColumnNameAttribute(string columnName)
        {
            _columnName = columnName;
        }
        /// <summary>
        /// Name for columnName.
        /// </summary>
        public string ColumnName
        {
            get
            {
                return (_columnName);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark a name for a columnName in a database tableName.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class DefaultValueAttribute : Attribute
    {
        object _DefaultValue;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="defaultValue">Name for default Value.</param>
        public DefaultValueAttribute(object defaultValue)
        {
            _DefaultValue = defaultValue;
        }
        /// <summary>
        /// Name for columnName.
        /// </summary>
        public object DefaultValue
        {
            get
            {
                return (_DefaultValue);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark a name for a columnName in a database tableName.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class TableNameIdentityAttribute : Attribute
    {
        string _tableName;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tableName">Name for columnName.</param>
        public TableNameIdentityAttribute(string tableName)
        {
            _tableName = tableName;
        }
        /// <summary>
        /// Name for table.
        /// </summary>
        public string TableName
        {
            get
            {
                return (_tableName);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark a name for a columnName in a database tableName.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class RelatedTableAttribute : Attribute
    {
        private readonly Type _related;
        private readonly string _value_related;
        private readonly string _display_related;

        public RelatedTableAttribute(Type related, string value_related, string display_related)
        {
            _related = related;
            _value_related = value_related;
            _display_related = display_related;
        }
        /// <summary>
        /// Name for table.
        /// </summary>
        public Type Related
        {
            get
            {
                return (_related);
            }
        }

        public string Value_related
        {
            get { return _value_related; }
        }

        public string Display_related
        {
            get { return _display_related; }
        }
    }

    /// <summary>
    /// Attribute tag to mark a name for a sequence in Oracle.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class SequenceNameAttribute : Attribute
    {
        string _sequenceName;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="sequenceName">Name for sequenceName.</param>
        public SequenceNameAttribute(string sequenceName)
        {
            _sequenceName = sequenceName;
        }
        /// <summary>
        /// Name for table.
        /// </summary>
        public string SequenceName
        {
            get
            {
                return (_sequenceName);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark a name for a sequence in Oracle.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class ComputedColumnAttribute : Attribute
    {
        string _ComputedColumn;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="ComputedColumn">Name for ComputedColumn.</param>
        public ComputedColumnAttribute(string ComputedColumn)
        {
            _ComputedColumn = ComputedColumn;
        }
        /// <summary>
        /// Name for table.
        /// </summary>
        public string ComputedColumn
        {
            get
            {
                return (_ComputedColumn);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark a name for a sequence in Oracle.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class ParentTypeAttribute : Attribute
    {
        Type _parentType;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="parentType">Type for parent table.</param>
        public ParentTypeAttribute(Type parentType)
        {
            _parentType = parentType;
        }
        /// <summary>
        /// Name for table.
        /// </summary>
        public Type ParentType
        {
            get
            {
                return (_parentType);
            }
        }
    }

    /// <summary>
    /// Attribute tag to mark columnName as from Flat File.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class FlatFileColumnAttribute : Attribute
    {
        int _initial, _final;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="initial">Initial columnName on line.</param>
        /// <param name="final">Final columnName on line.</param>
        public FlatFileColumnAttribute(int initial, int final)
        {
            _initial = initial;
            _final = final;
        }
        /// <summary>
        /// Initial columnName on line.
        /// </summary>
        public int Initial
        {
            get
            {
                return (_initial);
            }
        }
        /// <summary>
        /// Final columnName on line.
        /// </summary>
        public int Final
        {
            get
            {
                return (_final);
            }
        }

    }

    /// <summary>
    /// Attribute tag to establish the maximum date allowed, with associated description.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class MaxDateAttribute : Attribute
    {
        int  _days;
        string _description;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="days">Number of days</param>
        /// <param name="description">Error description</param>
        public MaxDateAttribute(int days, string description)
        {
            _days = days;
            _description = description;
        }
        /// <summary>
        /// Returns if date is less than the maximum.
        /// </summary>
        /// <param name="date">Date to compare</param>
        /// <returns>True if valid, false if not.</returns>
        public bool IsValid(DateTime date)
        {
            if (DateTime.Compare(date, DateTime.Now.AddDays(_days)) > 0)
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// Error description
        /// </summary>
        public string Description
        {
            get
            {
                return (_description);
            }
        }
        /// <summary>
        /// Error days
        /// </summary>
        public int Days
        {
            get
            {
                return (_days);
            }
        }
    }

    /// <summary>
    /// Attribute tag to associate a regular expression to the columnName.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class RegExpAttribute : Attribute
    {
        string _expression;
        string _description;
        Type _objectType;
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="expression">Regular expression.</param>
        public RegExpAttribute(string expression)
        {
            _expression = expression;
            _description = "";
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="expression">Regular expression.</param>
        /// <param name="description">Error message.</param>
        public RegExpAttribute(string expression, string description)
        {
            _expression = expression;
            _description = description;
        }
   
        /// <summary>
        /// Constructor for date.
        /// </summary>
        /// <param name="objectType">Type of object. Currently only Date is handled.</param>
        public RegExpAttribute(Type objectType)
        {
            // if object type is DateTime, set pre-defined regex.
            _objectType = objectType;
            if (_objectType != null && _objectType.Name == "DateTime")
            {
                _expression = @"^((((((0?[13578])|(1[0-2]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|([1-2][0-9]))))[\-\/\s]?\d{2}(([02468][048])|([13579][26])))|(((((0?[13578])|(1[0-2]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))[\-\/\s]?\d{2}(([02468][1235679])|([13579][01345789]))))(\s(((0?[1-9])|(1[0-2]))\:([0-5][0-9])((\s)|(\:([0-5][0-9])\s))([AM|PM|am|pm]{2,2})))?$";
                _description = "Invalid Date";
            }
        }

        /// <summary>
        /// Regular expression.
        /// </summary>
        public string Expression
        {
            get	{ return _expression; }
        }

        /// <summary>
        /// Error message.
        /// </summary>
        public string Description
        {
            get	{ return _description; }
        }

        /// <summary>
        /// Error message.
        /// </summary>
        public Type ObjectType
        {
            get	{ return _objectType; }
        }
    }

    /// <summary>
    /// Class defining how columnName value should be formatted. Uses regular expression for formatting.
    /// </summary>
    /// <example>
    /// This attribute formats Social Security Number for a columnName:
    /// <code>DisplayFormat("([0-9]{3})([0-9]{2})([0-9]{4})", "$1-$2-$3")</code>
    /// </example>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class DisplayFormatAttribute : Attribute
    {
        string _expression;
        string _format;
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="expression">Regular expression.</param>
        /// <param name="format">Formatting expression for regex.</param>
        public DisplayFormatAttribute(string expression, string format)
        {
            _expression = expression;
            _format = format;
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="format">Formatting expression for format provider.</param>
        public DisplayFormatAttribute(string format)
        {
            _expression = "";
            _format = format;
        }
        /// <summary>
        /// Regular expression.
        /// </summary>
        public string Expression
        {
            get
            {
                return (_expression);
            }
        }
        /// <summary>
        /// Formatting expression for regex.
        /// </summary>
        public string Format
        {
            get
            {
                return (_format);
            }
        }
    }

    /// <summary>
    /// Class used to un-format formatted values so they can be 
    /// saved to the database.
    /// </summary>
    /// <example><code>DisplayUnFormat("-+")</code>Will remove all "-" and "+" from the string.</example>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class DisplayUnFormatAttribute : Attribute
    {
        string _expression;
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="expression">Regular expression.</param>
        public DisplayUnFormatAttribute(string expression)
        {
            _expression = expression;
        }
        /// <summary>
        /// Regular expression.
        /// </summary>
        public string Expression
        {
            get
            {
                return (_expression);
            }
        }
    }

    /// <summary>
    /// Attribute to use on properties which return a weakly typed collection.
    /// This attribute will tell the property descriptor construction code to construct a list of 
    /// properties of the type set as the value of the attribute.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class TypeContainedAttribute : Attribute
    {
        #region Class Member Declarations
        private Type    _typeContainedInCollection;
        private string    _typeContainedInCollectionAlias;
        #endregion
        /// <summary>
        /// CTor
        /// </summary>
        /// <param name="typeContainedInCollection">The type of the objects contained in the collection
        /// returned by the property this attribute is applied to.</param>
        public TypeContainedAttribute(Type typeContainedInCollection)
        {
            _typeContainedInCollection = typeContainedInCollection;
            if (typeContainedInCollection != null)
                _typeContainedInCollectionAlias = typeContainedInCollection.Name;
        }
        /// <summary>
        /// CTor
        /// </summary>
        /// <param name="typeContainedInCollection">The type of the objects contained in the collection
        /// returned by the property this attribute is applied to.</param>
        /// <param name="typeContainedInCollectionAlias">The type of the objects contained in the collection	
        /// returned by the property this attribute is applied to.</param>
        public TypeContainedAttribute(Type typeContainedInCollection, string typeContainedInCollectionAlias)
        {
            _typeContainedInCollection = typeContainedInCollection;
            _typeContainedInCollectionAlias = typeContainedInCollectionAlias;
        }

        #region Class Property Declarations
        /// <summary>
        /// Gets typeContainedInCollection, the type set in the constructor
        /// </summary>
        public Type TypeContainedInCollection
        {
            get
            {
                return _typeContainedInCollection;
            }
        }
        /// <summary>
        /// Gets TypeContainedInCollectionAlias, the type set in the constructor
        /// </summary>
        public string TypeContainedInCollectionAlias
        {
            get
            {
                return _typeContainedInCollectionAlias;
            }
        }
        #endregion
    }

    /// <summary>
    /// Attribute to use on properties to make them hidden for complex databinding.
    /// This attribute will tell the property descriptor construction code to skip this property 
    /// for databinding purposes, the property will then not show up in the bound control.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class HiddenForDataBindingAttribute : Attribute
    {
        #region Class Member Declarations
        private bool    _isHidden;
        #endregion
        /// <summary>
        /// CTor
        /// </summary>
        public HiddenForDataBindingAttribute()
        {

        }

        /// <summary>
        /// CTor
        /// </summary>
        /// <param name="isHidden">Set to true if the property this attribute is applied 
        /// to should not be used in a complex databinding scenario. Default: false</param>
        public HiddenForDataBindingAttribute(bool isHidden)
        {
            _isHidden = isHidden;
        }

        #region Class Property Declarations
        /// <summary>
        /// Gets isHidden
        /// </summary>
        public bool IsHidden
        {
            get
            {
                return _isHidden;
            }
        }
        #endregion
    }
}